package gek.uni.aspirant.ocl.parser.ocl.visitors;

import gek.uni.aspirant.ocl.parser.mptree.tree.INode;
import gek.uni.aspirant.ocl.parser.mptree.tree.RootNode;
import gek.uni.aspirant.ocl.parser.mptree.tree.Utils;
import gek.uni.aspirant.ocl.parser.mptree.tree.VariableNode;
import gek.uni.aspirant.ocl.parser.mptree.tree.utils.OfflineUtils;

import java.util.List;

import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.expressions.IteratorExp;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.utilities.AbstractVisitor;
import org.eclipse.ocl.utilities.PredefinedType;

public class VariableNameTreeBuilder
    extends
    AbstractVisitor<INode, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> {

  @Override
  public INode visitVariableExp(VariableExp<EClassifier, EParameter> v) {
    String originalName = v.getName();
    String variableName = OfflineUtils.createPrologVariableName(originalName);
    String typeName = v.getType().getInstanceTypeName();

    VariableNode node = new VariableNode(variableName, typeName, originalName);
    return node;
  }

  @Override
  protected INode handlePropertyCallExp(
      PropertyCallExp<EClassifier, EStructuralFeature> callExp,
      INode sourceResult, List<INode> qualifierResults) {
    String sourceName = sourceResult.getName();

    String originalPropName = callExp.getReferredProperty().getName();

    String prologPropertyName = OfflineUtils.createPrologVariableName(originalPropName);
    String propertyName = sourceName + "_" + prologPropertyName;
    String typeName = callExp.getType().getInstanceTypeName();
    VariableNode node = new VariableNode(propertyName, typeName,
        originalPropName);
    sourceResult.addChild(node);

    return node;
  }

  @Override
  protected INode handleOperationCallExp(
      OperationCallExp<EClassifier, EOperation> callExp, INode sourceResult,
      List<INode> argumentResults) {

    // int operationCode = callExp.getOperationCode();
    //        
    // String opName = getOperationName(operationCode);
    // String typeName = callExp.getType().getInstanceClassName();
    // MethodNode methodNode = new MethodNode(opName, typeName, "", null);
    // sourceResult.addChild(methodNode);
    //        
    RootNode result = new RootNode(Utils.generateUniqueLong().toString());

    result.addChild(sourceResult.getRoot());

    for (INode argNode : argumentResults) {
      result.addChild(argNode);
    }

    return result;
  }

  @Override
  protected INode handleIteratorExp(
      IteratorExp<EClassifier, EParameter> callExp, INode sourceResult,
      List<INode> variableResults, INode bodyResult) {
    return sourceResult;
  }

  /**********************************************************************
   * VISIT - METHODS
   *********************************************************************/

  private String getOperationName(int opCode) {
    switch (opCode) {
    case PredefinedType.SIZE:
      return PredefinedType.SIZE_NAME;
    case PredefinedType.LESS_THAN:
      return PredefinedType.LESS_THAN_NAME;
    case PredefinedType.EQUAL:
      return PredefinedType.EQUAL_NAME;
    case PredefinedType.GREATER_THAN:
      return PredefinedType.GREATER_THAN_NAME;
    case PredefinedType.OCL_IS_UNDEFINED:
      return PredefinedType.OCL_IS_UNDEFINED_NAME;
    case PredefinedType.IS_EMPTY:
      return PredefinedType.IS_EMPTY_NAME;
    case PredefinedType.AND:
      return PredefinedType.AND_NAME;
    case PredefinedType.OR:
      return PredefinedType.OR_NAME;
    case PredefinedType.IMPLIES:
      return PredefinedType.IMPLIES_NAME;
    default:
      return "";
    }
  }
}
